package com.surrealbananapeel.battleship;
/**
 * This class represents the client application that connects to the server.
 *  * 
 * @author Emmett Little and Alberto Saavedra
 */
public class BattleshipClient
{
    private Connector connector;
    private Shooter shooter;
    private Layout layout;
    private Match match;
    private Mode currentMode;
    static final int HEIGHT = 10;
    static final int WIDTH = 10;
    static final int DEPTH = 10;
    static final String loginRequest = "Surreal Banana Peel|333typsy";
    //static final String loginRequest = "Surreal Banana Peel|TEST_GAME";
    
    public static enum Mode
    {
        WINGAME, WINMATCH, LOSEGAME, LOSEMATCH, HIT, MISS, FAIL
    };
    
    /**
     * Creates an instance of this class by getting an instance of a match to be played and 
     * starts the handshake transmission between server and client.
     */
    public BattleshipClient()
    {
        this.match = Match.getInstance();
        
        start();
    }
    
    /**
     * This method starts the handshake transmission of the game.
     */
    private void start()
    {       
        do
        {
            String response = null;
            currentMode = null;
            
            connect();
            layoutState();
            
            do
            {
                response = firingSate();
                if(currentMode != Mode.FAIL)
                {
                	currentMode = interpret(response);
                }                       
            } while (currentMode == Mode.HIT || currentMode == Mode.MISS);
            
            if (currentMode == Mode.WINGAME || currentMode == Mode.LOSEGAME)
            {
                restartGame(response);
            }
            else if (currentMode == Mode.WINMATCH
                    || currentMode == Mode.LOSEMATCH)
            {
                endMatch(response);
            }
            
        } while (currentMode == Mode.WINGAME || currentMode == Mode.LOSEGAME);
        
    }
    
    /**
     * This method is called after winning or loosing a game. The method 
     * closes the communication of the client.
     * 
     * @param message A string with the response from the server.
     */
    private void restartGame(String message)
    {       
        connector.close(); 
        if(currentMode == Mode.WINGAME){
        	match.incrementWinGames();
        }else{
        	match.incrementLoseGames();
        }
        System.out.println(message);    
    }
    
    /**
     * This method is called after winning or loosing a match. 
     *  
     * @param message A string with the response from the server.
     */
    private void endMatch(String message)
    {
        connector.close();

        if(currentMode == Mode.WINMATCH){
        	match.incrementWinGames();
        }else{
        	match.incrementLoseGames();
        }
        
        System.out.println(message);
        System.out.println("Games played: " + match.getNumberOfGames());
        System.out.println("Games won: " + match.getNumberOfWinGames());
        System.out.println("Games lost: " + match.getNumberOfLoseGames());
    }
    
    /**
     * This method creates a new communication with the server, and initialises 
     * the shooter AI.
     */
    private void connect()
    {
        connector = new Connector();
        shooter = new Shooter();
        System.out.println(connector.getResponse(loginRequest));
    }
    
    /**
     * This method creates a new layout of the ships for a new game, prepares the game 
     * for the match.
     */
    private void layoutState()
    {
        layout = new Layout();
        this.match.startNewGame();
        System.out.println(connector.getResponse(layout.toString()));
    }
    
    /**
     * This method is called every time the player is shooting. It stores 
     * the fires made and received for statistical purposes.
     * 
     * @return A string with the server response.
     */
    private String firingSate()
    {
        String response = null;
        Point point = shooter.shoot();
        Point opponentPoint = null;
        Point.State state = null;
        response = connector.getResponse(point.toString());
        String[] responseSplit = getTokens(response);
        if(responseSplit == null){
        	currentMode = Mode.FAIL;
        	return null;
        }
                
        if (!responseSplit[1].equals(""))
        {
            state = Point.State.DETECTED;
        }
        else if (responseSplit[0].equals("HIT"))
        {
            state = Point.State.HIT;
        }
        else
        {
            state = Point.State.MISS;
        }
        
        if (!responseSplit[2].equals("")
                && responseSplit[2].matches("^[0-9],[0-9],[0-9]"))
        {
            opponentPoint = new Point(responseSplit[2]);
            this.match.updateStats(opponentPoint, 0);
        }
        point.setState(state);
        this.match.updateStats(point, 1);
        this.match.updateCurrentGame(point, responseSplit[1]);
        
        
        
        return response;
    }
    
    /**
     * This method takes a string response from the server, and return the
     * current mode of the game.
     * 
     * @param message A string representing the response of the game.
     * @return The current mode of the game.
     */
    public Mode interpret(String message)
    {
        String tokens[] = getTokens(message);
                	       
        String responseCode = tokens[0];
        String details1 = tokens[1];
        
        
        if (responseCode.compareToIgnoreCase("HIT") == 0)
        {
            return Mode.HIT;
        }
        else if (responseCode.compareToIgnoreCase("MISS") == 0)
        {
            return Mode.MISS;
        }
        else if (responseCode.compareToIgnoreCase("WIN") == 0)
        {
            if (details1.compareTo("GAME") == 0)
            {
                return Mode.WINGAME;
            }
            return Mode.WINMATCH;
        }
        else if (responseCode.compareToIgnoreCase("LOSE") == 0)
        {
            if (details1.compareTo("GAME") == 0)
            {
                return Mode.LOSEGAME;
            }
            return Mode.LOSEMATCH;
        }
        return null;
    }
    
    /**
     * This method returns an array of tokens given a server response message.
     * 
     * @param message A string 
     * @return an array with the tokens of the server response.
     */
    private String[] getTokens(String message)
    {
        String tokens[] = null;
        if (message != null && !message.isEmpty())
        {
            // Matching Responses
            tokens = message.split("\\|");
            if (tokens.length == 3)
            {
                return tokens;
            }
            else if (tokens.length == 1)
            {
                String tmp[] = { tokens[0], "", "" };
                return tmp;
            }
            else if (tokens.length == 2)
            {
                String tmp[] = { tokens[0], tokens[1], "" };
                return tmp;
            }
        }
        return null;
    }
    
}
